#include "serial_port.h"

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <termios.h>
#include <unistd.h>

#include <cstring>
#include <iostream>

// 构造  初始化串口名
SerialPort::SerialPort(const std::string& port_name, const SerialConfig& config)
    : port_name_(port_name), is_open_(false) {
  if (!OpenSerialPort(config)) {
    std::cerr << "串口打开失败" << std::endl;
    exit(1);
  }
}

SerialPort::~SerialPort() {
  if (IsOpen())
    close(port_fd_);
  is_open_ = false;
  std::cout << "关闭串口成功" << std::endl;
}

bool SerialPort::OpenSerialPort(const SerialConfig& config) {
  if (IsOpen()) {
    std::cout << "该" << port_name_ << "已经被打开了" << std::endl;
    return false;  // 已经打开了
  }
  port_fd_ = open(
      port_name_.c_str(),
      O_RDWR | O_NOCTTY);  // 以读方式打开并且不将串口设备作为当前进程的控制终端
  if (port_fd_ < 0) {
    std::cout << "打开串口失败了" << port_name_ << std::endl;
    return false;
  }
  int flags = fcntl(port_fd_, F_GETFL, 0);  // 获取当前文件状态标志
  if (flags == -1) {
    perror("fcntl get flags failed");
    close(port_fd_);
    return false;
  }
  if (fcntl(port_fd_, F_SETFL, flags | O_NONBLOCK) ==
      -1) {  // 追加非阻塞标志 O_NONBLOCK
    perror("fcntl set non-block failed");
    close(port_fd_);
    return false;
  }
  // 将config设置进去
  serial_config_ = config;
  if (!UpdateConfiGuration()) {
    std::cout << "打开串口失败了" << std::endl;
    close(port_fd_);
    return false;
  }
  is_open_ = true;
  return true;
}

bool SerialPort::IsOpen() {
  return is_open_;
}

bool SerialPort::UpdateConfiGuration() {
  // 获取配置信息
  struct termios tty;
  if (tcgetattr(port_fd_, &tty) != 0) {
    // 获取失败
    std::cout << "获取串口配置信息失败" << std::endl;
    close(port_fd_);
    return false;
  }
  if (!ConfigureAllSettings(&tty)) {
    return false;
  }
  tcflush(port_fd_, TCIOFLUSH);
  if (tcsetattr(port_fd_, TCSANOW, &tty) != 0) {
    std::cout << "设置配置失败" << std::endl;
    return false;
  }
  return true;
}

bool SerialPort::ConfigureAllSettings(struct termios* tty) {
  speed_t speed;
  switch (serial_config_.baut_rate) {
    case 1200: speed = B1200; break;
    case 2400: speed = B2400; break;
    case 4800: speed = B4800; break;
    case 9600: speed = B9600; break;
    case 19200: speed = B19200; break;
    case 38400: speed = B38400; break;
    case 57600: speed = B57600; break;
    case 115200: speed = B115200; break;
    default: return false;
  }
  cfsetispeed(tty, speed);
  cfsetospeed(tty, speed);

  // 数据位
  tty->c_cflag &= ~CSIZE;
  switch (serial_config_.data_bits) {
    case DataBits::FIVE: tty->c_cflag |= CS5; break;
    case DataBits::SIX: tty->c_cflag |= CS6; break;
    case DataBits::SEVEN: tty->c_cflag |= CS7; break;
    case DataBits::EIGHT: tty->c_cflag |= CS8; break;
    default: return false;
  }

  // 停止位
  switch (serial_config_.stop_bits) {
    case StopBits::ONE: tty->c_cflag &= ~CSTOPB; break;
    case StopBits::TWO: tty->c_cflag |= CSTOPB; break;
    default: return false;
  }

  // 校验位
  switch (serial_config_.parity) {
    case Parity::NONE: tty->c_cflag &= ~PARENB; break;
    case Parity::EVEN:
      tty->c_cflag |= PARENB;
      tty->c_cflag &= ~PARODD;
      break;
    case Parity::ODD: tty->c_cflag |= (PARENB | PARODD); break;
    default: return false;
  }

  // 流控制
  tty->c_cflag &= ~CRTSCTS;
  tty->c_iflag &= ~(IXON | IXOFF | IXANY);
  switch (serial_config_.flow_control) {
    case FlowControl::NONE: break;
    case FlowControl::HARDWARE: tty->c_cflag |= CRTSCTS; break;
    case FlowControl::SOFTWARE: tty->c_iflag |= (IXON | IXOFF); break;
    default: return false;
  }

  // 基本设置
  tty->c_cflag |= (CLOCAL | CREAD);
  tty->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL);
  tty->c_oflag &= ~OPOST;
  tty->c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

  // 超时设置 - 改为可配置
  tty->c_cc[VMIN] = 0;
  tty->c_cc[VTIME] = 5;

  return true;
}

bool SerialPort::OpenSerialPort() {
  return OpenSerialPort(serial_config_);
}

bool SerialPort::SetBautRate(int bautrate) {
  // 1.获取原有配置
  termios tty;
  if (tcgetattr(port_fd_, &tty) != 0)
    return false;
  speed_t speed;
  switch (bautrate) {
    case 1200: speed = B1200; break;
    case 2400: speed = B2400; break;
    case 4800: speed = B4800; break;
    case 9600: speed = B9600; break;
    case 19200: speed = B19200; break;
    case 38400: speed = B38400; break;
    case 57600: speed = B57600; break;
    case 115200: speed = B115200; break;
    default: std::cout << "不存在该波特率" << std::endl; return false;
  }
  // 设置新配置
  cfsetispeed(&tty, speed);
  cfsetospeed(&tty, speed);
  // 3.更新配置
  return tcsetattr(port_fd_, TCSANOW, &tty) == 0;
}

bool SerialPort::ChangeSerialPortConfig(const SerialConfig& config) {
  if (!IsOpen()) {
    std::cout << "串口未被打开" << std::endl;
    return false;
  }
  serial_config_ = config;
  return UpdateConfiGuration();
}

SerialConfig SerialPort::GetSerialPortConfig() {
  return serial_config_;
}

ssize_t SerialPort::Write(const uint8_t* data, size_t data_size) {
  if (!IsOpen()) {
    std::cout << "串口未被打开" << std::endl;
    return -1;
  }
  ssize_t length = ::write(port_fd_, data, data_size);
  if (length < 0) {
    std::cout << "写入失败" << std::endl;
    return -1;
  }
  return length;
}

ssize_t SerialPort::Read(uint8_t* buffer, size_t max_length) {
  if (!IsOpen()) {
    std::cout << "串口未被打开" << std::endl;
    return -1;
  }
  ssize_t length = ::read(port_fd_, buffer, max_length);
  if (length < 0) {
    // 非阻塞模式下无数据（正常情况，返回0表示“无数据”）
    if (errno == EAGAIN || errno == EWOULDBLOCK) {
      return 0;
      //  其他错误
      std::cerr << "Read: 系统调用失败，错误码=" << errno << "，原因："
                << strerror(errno) << std::endl;
      return -1;
    }
  }
  return length;
}
